package sc;

/** TPS encapsulates a game loops ticks per second management. */
public final class TPS {
	
	private int avg_tps;
	private long skip_time;
	private long spare_time_stats;
	private long spare_time;
	private int max_tps;
	private int ticks;
	private long last_update;
	private long last_tick;
	private long now;
	private long update_interval;
	private double update_seconds;
	
	/**
	 * Constructs an instance of FPS.
	 * 
	 * Maximum ticks per second defaults to 100. Use setTPS to change this.
	 */
	public TPS() {
		this(100);
	}
	
	/**
	 * Constructs an instance of FPS with a specified maximum tick per second.
	 */
	public TPS(int tps) {
		setTPS(tps);
		setStatisticsInterval(3000);
	}
	
	/**
	 * Sets the maximum ticks per second.
	 * 
	 * @param tps
	 * 			ticks per second
	 */
	public void setTPS(int tps) {
		max_tps = tps;
		skip_time = 1000L / tps;
	}
	
	/**
	 * Sets the statistics update interval.
	 * 
	 * @param interval
	 * 			milliseconds between updates
	 */
	public void setStatisticsInterval(long interval) {
		update_interval = interval;
		update_seconds = update_interval / 1000;
	}

	/**
	 * Checks if a tick is due.
	 * 
	 * @return true when due; false otherwise
	 */
    public boolean tick() {
    	
    	// Get current time and calculate time till the next tick
    	now = System.currentTimeMillis();
    	spare_time = skip_time - (now - last_tick);
    	
    	// Is a tick due yet?
        if (spare_time > 0) {
            // No, so give some CPU back to the system and return false
        	spare_time_stats = (spare_time_stats + spare_time) / 2;
            try {Thread.sleep(spare_time);} catch (InterruptedException e) {}
        	return false;
        }
        
        // Yes, so tick and update statistics
        last_tick = now;
        ticks++;

        if ((now - update_interval) > last_update) {
        	last_update = now;
        	
        	// Calculate average ticks per second
        	avg_tps = (int)(ticks / update_seconds);
        	ticks = 0;
        		
        	// Adjust skip_time to better match desired tps
        	skip_time += avg_tps - max_tps;
        	if (skip_time < 0) {
        		skip_time = 0;
        	}
        }
        	
        return true;
    } 
	
	/** Gets the average ticks per second currently being achieved. */
	public int avgTPS() {
		return avg_tps;
	}
	
	/** Gets the time skipped between ticks. */
	public long skipTime() {
		return skip_time;
	}
	
	/** Gets the time to spare before next tick. */
	public long spareTime() {
		return spare_time_stats;
	}
}
